package com.ybt.communicate.tcp.client;

import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

import org.apache.log4j.Logger;

import com.ybt.communicate.core.IConnection;
import com.ybt.communicate.core.IProtocol;
import com.ybt.communicate.core.exception.ProtocolException;
import com.ybt.communicate.tcp.configration.TCPRuntimeConfig;
import com.ybt.communicate.tcp.utils.SocketUtil;

public class TCPClientLong implements IProtocol {
    private static Logger logger = Logger.getLogger(TCPClientLong.class);
    private TCPRuntimeConfig config;
    private List<IConnection> idlePool;
    private volatile boolean start;
    private volatile UUID notifyStartId;
    private volatile UUID currStartId;
    private Monitor monitor;

    public TCPClientLong(TCPRuntimeConfig rconfig) {
        super();
        this.config = rconfig;
        idlePool = new ArrayList<IConnection>();
    }

    public synchronized boolean isStart() {
        // TODO Auto-generated method stub
        return start;
    }

    public synchronized void start() throws ProtocolException {
        if (start) {
            // 客户端已经启动
            if (logger.isDebugEnabled()) {
                logger.debug("the client connecting to " + config.getIp()+":"+config.getPort()
                        + " is already started");
            }
            return;
        }
        start = true;
        // 设置初始批次标记
        currStartId = UUID.randomUUID();
        notifyStartId = currStartId;

        // 初始化守护线程
        monitor = new Monitor();
        // 启动守护线程
        monitor.start();
        if (logger.isDebugEnabled()) {
            logger.debug("the client connecting to " + config.getIp()+":"+config.getPort()
                    + " has started ...");
        }
    }

    public synchronized void stop() throws ProtocolException {
        // TODO Auto-generated method stub
        if (!start) {
            // 客户端已经启动
            if (logger.isDebugEnabled()) {
                logger.debug("the client connecting to " + config.getIp()+":"+config.getPort()
                        + " is already stopped");
            }
            return;
        }
        start = false;

        // 清空连接池
        synchronized (idlePool) {
            // 避免多cpu之间的竞争
            this.clearIdlePool();
            if (logger.isDebugEnabled()) {
                logger.debug("the client connecting to " + config.getIp()+":"+config.getPort()
                        + " has stopped ...");
            }
        }

        // 停止守护线程的工作
        monitor.setDone(false);
        monitor.notify();
    }

    public synchronized IConnection createConnection() throws ProtocolException {
        return this.createConnection(Long.MAX_VALUE);
    }

    public synchronized IConnection createConnection(long timeout) throws ProtocolException {
        // TODO Auto-generated method stub
        if (!start) {
            throw new ProtocolException("the client connecting to "
                    + config.getIp()+":"+config.getPort() + " hasn't been started ...");
        }
        synchronized (idlePool) {
            while (idlePool.size() < 1) {
                // 没有空闲连接，等待
                try {
                    idlePool.wait(timeout);
                } catch (InterruptedException e) {
                    throw new ProtocolException(e);
                }
            }
            // 有空闲连接了
            return idlePool.remove(0);

        }
    }

    public synchronized void notify(UUID startId, Exception e) {
        if (!start) {
            // 连接池已经关闭
            return;
        }

        synchronized (monitor) {
            // 获取守护线程锁
            this.notifyStartId = startId;
            // 唤醒守护线程，让其重新初始化连接池
            monitor.notify();
        }
    }

    public synchronized void relaseConnnect(TCPClientLongConnection conn)
            throws ProtocolException {
        if (!start) {
            // 连接池已经关闭，关闭连接即可
            conn.destroy();
        }
        synchronized (idlePool) {
            if (conn.getStartId().equals(currStartId)) {
                // 批次号相同，则放回连接池
                idlePool.add(conn);

                // 有空闲连接了，需要唤醒等待的线程
                idlePool.notify();
            } else {
                // 该批次号已经作废
                conn.destroy();
            }
        }
    }

    // 启动连接池守护线程
    private class Monitor extends Thread {
        // 退出线程标志
        private volatile boolean done = true;

        @Override
        public void run() {
            while (done) {
                // 守护线程没有停止

                synchronized (this) {
                    // 此处同步只是用作，出现异常时唤醒使用

                    if (currStartId.equals(notifyStartId)) {
                        // 第一个异常通知，需要重新初始化连接池

                        synchronized (idlePool) {
                            // 获取空闲池的锁

                            // 关闭现有空闲池中的链接
                            clearIdlePool();

                            // 更新启动批次标志
                            currStartId = UUID.randomUUID();
                            // 初始化空闲池中的链接
                            while (idlePool.size() < config.getWorkThread()) {
                                Socket s;
                                try {
                                    s = SocketUtil.createClientSocket(config);
                                    TCPClientLongConnection con = new TCPClientLongConnection(
                                            s, config, currStartId);
                                    con.setProtocol(TCPClientLong.this);
                                    idlePool.add(con);
                                } catch (Exception e) {
                                    logger.error("create connection to "
                                            + config.getIp() + ":"
                                            + config.getPort(), e);
                                }
                            }
                            // 池中有空闲连接了，唤醒等待线程
                            try {
                                idlePool.wait();
                            } catch (InterruptedException e) {
                                // TODO Auto-generated catch block
                                logger.error(e, e);
                            }
                        }
                    } else {
                        // 重复的异常唤醒，就不理他了。守护线程具体等待
                        try {
                            this.wait();
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            logger.error(e, e);
                        }
                    }
                }
                if (logger.isDebugEnabled()) {
                    logger
                            .debug("the demon thread for connection to "
                                    + config.getIp() + ":" + config.getPort()
                                    + " exit");
                }
            }

        }

        public void setDone(boolean done) {
            this.done = done;
        }

    };

    private void clearIdlePool() {
        // 关闭现有空闲池中的链接
        while (idlePool.size() > 0) {
            IConnection re = idlePool.remove(0);
            try {
                re.close();
            } catch (ProtocolException e) {
                // TODO Auto-generated catch block
                logger.error("closeing connection error", e);
            }
        }
    }

}
